AWS Step Functionsを利用し、S3バケット内の音声ファイルをAmazon Transcribeで文字起こししてみた

AWS Step Functionsを利用し、S3バケット内の音声ファイルをAmazon Transcribeで文字起こししてみた

Clock Icon2024.07.16

はじめに

AWS Step Functions ステートマシンを使用して、S3バケット内の音声ファイルをAmazon Transcribeで文字起こししてみました

システム構成は以下の通りです。

cm-hirai-screenshot 2024-07-12 21.20.39

Step Functions ステートマシンを起点として、S3バケット内の音声ファイルをTranscribeで文字起こしし、生成されたテキストファイルを別のS3バケットに保存します。

S3バケット作成

以下のS3バケットを2つ作成します。

  • 音声ファイル保存用(cm-hirai-audio-for-transcription)
  • 文字起こししたテキストファイルを保存用(cm-hirai-transcription-output)

バケット名以外の設定はすべてデフォルトのままとしています。

cm-hirai-screenshot 2024-07-12 21.22.44
cm-hirai-screenshot 2024-07-12 21.23.16

IAMポリシー作成

ステートマシン用のIAMポリシーを作成します。

cm-hirai-transcribejob-policyという名前で以下の権限で作成します。アカウントIDは各自で値を変えてください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::cm-hirai-transcription-output/*"
        },
        {
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::cm-hirai-audio-for-transcription/*"
        },
        {
            "Effect": "Allow",
            "Action": "transcribe:GetTranscriptionJob",
            "Resource": "arn:aws:transcribe:ap-northeast-1:123456789012:transcription-job/*"
        },
        {
            "Effect": "Allow",
            "Action": "transcribe:StartTranscriptionJob",
            "Resource": "*"
        }
    ]
}

ステートマシン作成

次に、Step Functions のステートマシンを作成します。

cm-hirai-screenshot 2024-07-12 21.48.15

まず、ステートマシンの定義画面からコードタブに移動し、以下のJSONコードをペーストします。

{
  "Comment": "Transcription job using Amazon Transcribe",
  "StartAt": "StartTranscriptionJob",
  "States": {
    "StartTranscriptionJob": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:transcribe:startTranscriptionJob",
      "Parameters": {
        "TranscriptionJobName.$": "$$.Execution.Name",
        "LanguageCode": "ja-JP",
        "Media": {
          "MediaFileUri.$": "$.inputFile"
        },
        "OutputBucketName": "cm-hirai-transcription-output",
        "OutputKey.$": "States.Format('{}.json', $$.Execution.Name)",
        "Settings": {
          "ShowSpeakerLabels": true,
          "MaxSpeakerLabels": 2
        }
      },
      "Next": "Wait",
      "Catch": [
        {
          "ErrorEquals": [
            "BadRequestException",
            "ConflictException",
            "InternalFailureException",
            "LimitExceededException"
          ],
          "Next": "TranscriptionJobFailed"
        }
      ]
    },
    "Wait": {
      "Type": "Wait",
      "Seconds": 1,
      "Next": "GetTranscriptionJob"
    },
    "GetTranscriptionJob": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:transcribe:getTranscriptionJob",
      "Parameters": {
        "TranscriptionJobName.$": "$.TranscriptionJob.TranscriptionJobName"
      },
      "Next": "CheckStatus",
      "Catch": [
        {
          "ErrorEquals": [
            "BadRequestException",
            "InternalFailureException",
            "LimitExceededException",
            "NotFoundException"
          ],
          "Next": "TranscriptionJobFailed"
        }
      ]
    },
    "CheckStatus": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.TranscriptionJob.TranscriptionJobStatus",
          "StringEquals": "COMPLETED",
          "Next": "TranscriptionJobSucceeded"
        },
        {
          "Variable": "$.TranscriptionJob.TranscriptionJobStatus",
          "StringEquals": "FAILED",
          "Next": "TranscriptionJobFailed"
        }
      ],
      "Default": "Wait"
    },
    "TranscriptionJobSucceeded": {
      "Type": "Succeed"
    },
    "TranscriptionJobFailed": {
      "Type": "Fail",
      "Cause": "Transcription job failed",
      "Error": "TranscriptionJobFailedException"
    }
  }
}
  • OutputBucketNameは、文字起こししたテキストファイルを保存するS3バケット名です。各自の環境に合わせて変更してください。
  • Transcribeジョブ名と文字起こししたテキストファイル名は、ステートマシン実行名と同一にしています。
    • ステートマシン実行名は、ステートマシンやその実行に関する情報を含むコンテキストオブジェクトから取得しています。

https://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/input-output-contextobject.html

ワークフローは以下の通りです。

cm-hirai-screenshot 2024-07-16 8.23.15

設定タブに移動し、ステートマシン名を入力します。

cm-hirai-screenshot 2024-07-12 21.51.50

画面右上の「作成」ボタンをクリックします。
IAMロールは自動的に作成されますが、TranscribeやS3へのアクセス権限は手動で追加する必要があります。
cm-hirai-screenshot 2024-07-12 21.52.31

ステートマシンの作成後、自動生成されたIAMロールに、先ほど作成したステートマシン用のIAMポリシーを適用します。
cm-hirai-screenshot 2024-07-12 21.54.00

テスト

音声ファイル保存用のS3バケット(cm-hirai-audio-for-transcription)にtest1.wavtest1.jsonを保存します。
cm-hirai-screenshot 2024-07-12 21.59.11

ステートマシンの入力として以下のJSONを設定し、実行します。
cm-hirai-screenshot 2024-07-16 8.36.14

{
  "inputFile": "s3://cm-hirai-audio-for-transcription/test1.wav"
}

ステートマシンが正常に実行完了しました。

cm-hirai-screenshot 2024-07-16 8.23.00

文字起こししたテキストファイルを保存するS3バケット(cm-hirai-transcription-output)には、ステートマシン実行名やTranscribeジョブ名と同じファイル名e3ac9297-120e-4bc7-8104-b12873717550.jsonで、以下の内容が保存されていました。

e3ac9297-120e-4bc7-8104-b12873717550.json
{
  "jobName": "",
  "accountId": "xxxxxxxxxxxx",
  "status": "COMPLETED",
  "results": {
    "transcripts": [
      { "transcript": "あこんにちは。もしもし?クラスメソッドテテストです" }
    ],
    "speaker_labels": {
      "segments": [
        {
          "start_time": "6.719",
          "end_time": "7.949",
          "speaker_label": "spk_0",
          "items": [
            {
              "speaker_label": "spk_0",
              "start_time": "6.829",
              "end_time": "7.079"
            },
            {
              "speaker_label": "spk_0",
              "start_time": "7.09",
              "end_time": "7.82"
            }
          ]
        },
        {
          "start_time": "8.479",
          "end_time": "10.479",
          "speaker_label": "spk_0",
          "items": [
            {
              "speaker_label": "spk_0",
              "start_time": "8.59",
              "end_time": "9.199"
            },
            {
              "speaker_label": "spk_0",
              "start_time": "9.5",
              "end_time": "9.869"
            },
            {
              "speaker_label": "spk_0",
              "start_time": "9.88",
              "end_time": "10.39"
            }
          ]
        },
        {
          "start_time": "11.649",
          "end_time": "13.93",
          "speaker_label": "spk_1",
          "items": [
            {
              "speaker_label": "spk_1",
              "start_time": "11.789",
              "end_time": "11.899"
            },
            {
              "speaker_label": "spk_1",
              "start_time": "12.92",
              "end_time": "13.35"
            },
            {
              "speaker_label": "spk_1",
              "start_time": "13.359",
              "end_time": "13.659"
            }
          ]
        }
      ],
      "channel_label": "ch_0",
      "speakers": 2
    },
    "items": [
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.897", "content": "あ" }],
        "start_time": "6.829",
        "end_time": "7.079",
        "speaker_label": "spk_0"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.996", "content": "こんにちは" }],
        "start_time": "7.09",
        "end_time": "7.82",
        "speaker_label": "spk_0"
      },
      {
        "type": "punctuation",
        "alternatives": [{ "confidence": "0.0", "content": "。" }],
        "speaker_label": "spk_0"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.998", "content": "もしもし" }],
        "start_time": "8.59",
        "end_time": "9.199",
        "speaker_label": "spk_0"
      },
      {
        "type": "punctuation",
        "alternatives": [{ "confidence": "0.0", "content": "?" }],
        "speaker_label": "spk_0"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.998", "content": "クラス" }],
        "start_time": "9.5",
        "end_time": "9.869",
        "speaker_label": "spk_0"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.997", "content": "メソッド" }],
        "start_time": "9.88",
        "end_time": "10.39",
        "speaker_label": "spk_0"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.459", "content": "テ" }],
        "start_time": "11.789",
        "end_time": "11.899",
        "speaker_label": "spk_1"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.996", "content": "テスト" }],
        "start_time": "12.92",
        "end_time": "13.35",
        "speaker_label": "spk_1"
      },
      {
        "type": "pronunciation",
        "alternatives": [{ "confidence": "0.999", "content": "です" }],
        "start_time": "13.359",
        "end_time": "13.659",
        "speaker_label": "spk_1"
      }
    ]
  }
}

別のケースとして、音声ファイルではない以下のJSONを入力して実行すると、エラーが発生します。

{
  "inputFile": "s3://cm-hirai-audio-for-transcription/test1.json"
}

cm-hirai-screenshot 2024-07-16 8.23.54

最後に

本記事では、AWS Step Functions ステートマシンを使用して、S3バケット内の音声ファイルをAmazon Transcribeで文字起こしする方法を紹介しました。

Step Functionsを利用することで、ローコードで実装やエラーハンドリングが可能となり、効率的なワークフロー構築が実現できます。

参照

https://docs.aws.amazon.com/ja_jp/transcribe/latest/APIReference/API_GetTranscriptionJob.html

https://docs.aws.amazon.com/ja_jp/transcribe/latest/APIReference/API_TranscriptionJob.html#transcribe-Type-TranscriptionJob-LanguageCode

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.